package com.coolw.thread.demo;

import org.junit.Before;
import org.junit.Test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * InheritableThreadLocal,子线程可以读取到父线程的数据
 * <pre>
 * InheritableThreadLocal重写了childValue、getMap、createMap三个方法
 * 1.在createMap方法中，new ThreadLocalMap(this, firstValue)赋值给了Thread类的变量inheritableThreadLocals
 * 2.创建Thread对象，构造方法中会调用init方法,inheritThreadLocals参数默认为true，则会将父线程的数据传递到子线程中
 * <pre>
 *
 * <p>关键源码{@link Thread}#init方法
 * <code>
 * Thread parent = currentThread();
 *
 * if (inheritThreadLocals && parent.inheritableThreadLocals != null)
 * this.inheritableThreadLocals =
 * ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
 * </code>
 *
 * @author coolw
 * @date 2024/1/9 19:40
 */
public class Demo21InheritableThreadLocal {

    /**
     * 线程池定义，固定核心线程和最大线程数为1
     */
    private static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(1);

    /**
     * 子线程无法获取到父线程的数据，仅存储当前线程的数据
     */
    //private static final ThreadLocal<String> ITL = new ThreadLocal<>();

    /**
     * 子线程可以获取到父线程的数据
     */
    private static final ThreadLocal<String> ITL = new InheritableThreadLocal<>();

    @Before
    public void before() {
        ITL.remove();
    }

    /**
     * 模拟子线程可以读取到父线程的数据
     */
    @Test
    public void testInheritableThreadLocal() {
        final String threadName = Thread.currentThread().getName();

        // 父线程赋值
        ITL.set("coolw");
        System.out.println("父线程赋值，val:" + ITL.get() + ",线程名:" + threadName);

        // 子线程
        new Thread(() -> getVal()).start();
    }

    /**
     * InheritableThreadLocal与线程池一起使用，子线程可能获取不到父线程的数据，只有创建线程的时候才会将父线程的数据据传递到子线程中
     * <p>
     * {@link InheritableThreadLocal}#createMap，会创建ThreadLocalMap对象，赋值给Thread类中的变量inheritableThreadLocals
     * <p>
     * 线程池中的线程是复用的，在复用线程情况下，不会执行{@link Thread}#init()方法，所以无法将父线程的数据据传递到子线程中
     */
    @Test
    public void testInheritableThreadLocalWithPool() throws InterruptedException {
        final String threadName = Thread.currentThread().getName();

        // 父线程第一次赋值
        ITL.set("coolw");
        System.out.println("父线程第一次赋值，val:" + ITL.get() + ",线程名:" + threadName);

        // 子线程，第一次会创建线程，父线程的变量会传递到子线程，所以能获取到父线程的数据
        EXECUTOR_SERVICE.submit(() -> getVal());

        // 父线程休眠1秒
        Thread.sleep(1000L);

        // 父线程第二次赋值
        ITL.set("hello");
        System.out.println("父线程第二次赋值，val:" + ITL.get() + ",线程名:" + threadName);
        // 子线程，第二次不会创建线程，会复用第一次创建的线程，所以父线程第二次赋值，不会传递到子线程中
        EXECUTOR_SERVICE.submit(() -> getVal());
    }

    private static void getVal() {
        final String threadName = Thread.currentThread().getName();
        System.out.println("子线程获取值，val:" + ITL.get() + ",线程名:" + threadName);
    }
}
